package com.czk.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author:ChenZhangKun
 * @Date: 2021/3/31 9:45
 */
public class CzkApplicationContext {
    /**
     * 传入的配置类字节码对象
     */
    private Class configClass;
    /**
     * 存储class的集合
     */
    private List<String> classPaths = new ArrayList<>(10);
    /**
     * 并发集合
     */
    private Map<String, BeanDefinition> map = new ConcurrentHashMap<>();
    /**
     * 单例池bean
     */
    private Map<String, Object> singletonBean = new ConcurrentHashMap<>();
    /**
     * clazz集合
     */
    private List<Class> beanClass = new ArrayList<>(10);
    /**
     * 存储后置处理器
     */
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>(10);

    /**
     * 构造方法
     */
    public CzkApplicationContext() {
    }

    public CzkApplicationContext(Class configClass) {
        this.configClass = configClass;
        // 拿到扫描路径
        String path = getPath(configClass);
        // 拿到所有的类全限定类路径
        scanPackage(path);
        // 得到字节码对象
        getClazz();
        // 初始化beanDefinition
        initBeanDefinition();
        // 扫描实例
        instanceSingleton();
    }

    /**
     * 扫描单例
     */
    private void instanceSingleton() {
        // 遍历
        for (String beanName : map.keySet()) {
            // 拿到对象包装类
            BeanDefinition beanDefinition = map.get(beanName);
            // 是否是单例
            if (Objects.equals(beanDefinition.getScape(), "singleton")) {
                // 单例
                Object bean = doCreateBean(beanName, beanDefinition);
                // 单例池是否含有
                if (!singletonBean.containsValue(bean)) {
                    singletonBean.put(beanName, bean);
                }
            }
        }

    }

    /**
     * 创建bean
     *
     * @param beanName
     * @param beanDefinition
     * @return
     */
    private Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
        // 实例化，得到对象
        Class clazz = beanDefinition.getClazz();
        // 反射得到对象
        Object object = null;
        try {
            object = clazz.getDeclaredConstructor().newInstance();
            // 属性填充，依赖注入
            Field[] fields = clazz.getDeclaredFields();
            // 遍历属性
            for (Field field : fields) {
                // 是否需要注入
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 关闭安全检查
                    field.setAccessible(true);
                    // 拿到名称
                    String name = field.getName();
                    // 拿到bean
                    Object bean = getBean(name);
                    // 注入 此时忽略了循环依赖的问题
                    field.set(object, bean);
                }
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        // 是否实现了name回调接口
        if (object instanceof BeanNameAware) {
            // 调用接口方法
            BeanNameAware beanNameAware = (BeanNameAware) object;
            beanNameAware.setBeanName(beanName);
        }
        // 初始化之前
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            object = beanPostProcessor.postProcessBeforeInitialization(object, beanName);
        }
        // 初始化
        if (object instanceof InitializingBean) {
            InitializingBean initializingBean = (InitializingBean) object;
            initializingBean.afterPropertiesSet();
        }
        return object;
    }

    /**
     * 初始化beanDefinition
     */
    private void initBeanDefinition() {
        for (Class clazz : beanClass) {
            // 是否被标记
            if (clazz.isAnnotationPresent(Component.class)) {
                // 是否实现后置处理器接口
                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                    // 实现了该接口
                    try {
                        BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                        // 添加到后置处理器的集合中
                        beanPostProcessorList.add(beanPostProcessor);
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                }

                BeanDefinition beanDefinition = new BeanDefinition();
                Component component = (Component) clazz.getAnnotation(Component.class);
                // 拿到beanName
                String beanName = component.value();
                if (Objects.equals("", beanName)) {
                    // 默认首字母小写
                    String simpleName = clazz.getSimpleName();
                    // 转为小写
                    simpleName = toLowercase(simpleName);
                    // 赋值
                    beanName = simpleName;
                }
                // 作用域
                if (clazz.isAnnotationPresent(Scope.class)) {
                    // 拿到注解
                    Scope scope = (Scope) clazz.getAnnotation(Scope.class);
                    // 拿到
                    String value = scope.value();
                    beanDefinition.setScape(value);
                } else {
                    // 单例
                    beanDefinition.setScape("singleton");
                }
                beanDefinition.setClazz(clazz);
                // 放入集合
                map.put(beanName, beanDefinition);
            }
        }

    }

    /**
     * 设置beanName 默认为类名的首字母小写
     *
     * @param simpleName
     * @return
     */
    private String toLowercase(String simpleName) {
        // 拿到第一个字母
        String first = simpleName.substring(0, 1);
        first = first.toLowerCase();
        String subString = simpleName.substring(1);
        simpleName = first + subString;
        return simpleName;
    }

    /**
     * 得到字节码对象
     */
    private void getClazz() {
        // 循环
        for (String classPath : classPaths) {
            // 去除.class
            String s = classPath.replaceAll(".class", "");
            // 反射
            try {
                Class<?> clazz = Class.forName(s);
                // 加入集合
                beanClass.add(clazz);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 拿到扫描的地址
     *
     * @param configClass
     * @return
     */
    public String getPath(Class configClass) {
        // 拿到注解
        boolean b = configClass.isAnnotationPresent(ComponentScan.class);
        if (!b) {
            throw new RuntimeException("注册出错");
        }
        // 拿到注解
        ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
        // 拿到路径
        String path = componentScan.value();
        return path;
    }

    /**
     * 扫描所有的.class
     *
     * @param path
     */
    private void scanPackage(String path) {
        // 拿到类加载器加载
        URL url = this.getClass().getClassLoader().getResource(path.replaceAll("\\.", "/"));
        // 拿到文件地址
        if (url != null) {
            String fileStr = url.getFile();
            // 转为文件
            File file = new File(fileStr);
            // 获取里面所有文件
            String[] list = file.list();
            if (list != null) {
                // 遍历
                for (String s : list) {
                    // 转为文件
                    File file1 = new File(fileStr + "/" + s);
                    // 是目录，则递归
                    if (file1.isDirectory()) {
                        scanPackage(path + "." + s);
                    } else {
                        classPaths.add(path + "." + s);
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        CzkApplicationContext applicationContext = new CzkApplicationContext();
        applicationContext.scanPackage("com.czk.demo");
        System.out.println(applicationContext.classPaths);
    }


    /**
     * 拿到bean
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        // 拿到对象描述对象
        BeanDefinition beanDefinition = map.get(beanName);
        if (beanDefinition.getScape().equals("singleton")) {
            // 单例的，从单例池获取
            Object obj = singletonBean.get(beanName);
            if (obj == null) {
                // 创建bean
                obj = doCreateBean(beanName, beanDefinition);
            }
            return obj;
        } else {
            // 多例的原型模式
            // 直接创建并返回
            Object bean = doCreateBean(beanName, beanDefinition);
            return bean;
        }
    }
}
